using System;
using System.Collections;
using System.IO;
using System.Data;
using System.Xml;
using umbraco.DataLayer;

using System.Text.RegularExpressions;
using umbraco.cms.businesslogic.cache;

namespace umbraco.cms.businesslogic.web
{
    /// <summary>
    /// Summary description for StyleSheet.
    /// </summary>
    public class StyleSheet : CMSNode
    {

        private string _filename = "";
        private string _content = "";
        private StylesheetProperty[] m_properties;
        static Guid moduleObjectType = new Guid("9f68da4f-a3a8-44c2-8226-dcbd125e4840");

        private static object stylesheetCacheSyncLock = new object();
        private static readonly string UmbracoStylesheetCacheKey = "UmbracoStylesheet";

        public string Filename
        {
            get { return _filename; }
            set
            {

                //move old file
                //File.Move(System.Web.HttpContext.Current.Server.MapPath(GlobalSettings.Path + "/../css/" + this.Text + ".css"), System.Web.HttpContext.Current.Server.MapPath(GlobalSettings.Path + "/../css/" + value + ".css"));
                _filename = value;
                SqlHelper.ExecuteNonQuery("update cmsStylesheet set filename = '" + _filename + "' where nodeId = " + base.Id.ToString());
                InvalidateCache();

            }
        }

        public string Content
        {
            get { return _content; }
            set
            {
                _content = value;
                SqlHelper.ExecuteNonQuery("update cmsStylesheet set content = '" + _content.Replace("'", "''") + "' where nodeId = " + base.Id.ToString());
                InvalidateCache();
            }
        }

        public StylesheetProperty[] Properties
        {
            get
            {
                if (m_properties == null)
                {
                    BusinessLogic.console.IconI[] tmp = this.ChildrenOfAllObjectTypes;

                    StylesheetProperty[] retVal = new StylesheetProperty[tmp.Length];
                    for (int i = 0; i < tmp.Length; i++)
                        retVal[i] = StylesheetProperty.GetStyleSheetProperty(this.ChildrenOfAllObjectTypes[i].Id);
                    m_properties = retVal;
                }
                return m_properties;
            }
        }

        //static bool isInitialized = false;

        public StyleSheet(Guid id)
            : base(id)
        {
            setupStyleSheet(true, true);
        }

        public StyleSheet(int id)
            : base(id)
        {
            setupStyleSheet(true, true);
        }

        public StyleSheet(int id, bool setupStyleProperties, bool loadContentFromFile)
            : base(id)
        {
            setupStyleSheet(loadContentFromFile, setupStyleProperties);
        }

        /// <summary>
        /// Used to persist object changes to the database. In Version3.0 it's just a stub for future compatibility
        /// </summary>
        public override void Save()
        {
            SaveEventArgs e = new SaveEventArgs();
            FireBeforeSave(e);
            if (!e.Cancel) {
                base.Save();
                FireAfterSave(e);
            }
        }


        private void setupStyleSheet(bool loadFileData, bool updateStyleProperties)
        {
            // Get stylesheet data
            IRecordsReader dr = SqlHelper.ExecuteReader("select filename, content from cmsStylesheet where nodeid = " + base.Id.ToString());
            if (dr.Read())
            {
                if (!dr.IsNull("filename"))
                    _filename = dr.GetString("filename");
                // Get Content from db or file 
                if (!loadFileData)
                {
                    if (!dr.IsNull("content"))
                        _content = dr.GetString("content");
                }
                else
                    if (File.Exists(System.Web.HttpContext.Current.Server.MapPath(GlobalSettings.Path + "/../css/" + this.Text + ".css")))
                    {

                        StreamReader re = File.OpenText(System.Web.HttpContext.Current.Server.MapPath(GlobalSettings.Path + "/../css/" + this.Text + ".css"));
                        string input = null;
                        bool read = true;
                        _content = string.Empty;
                        // NH: Updates the reader to support properties
                        bool readingProperties = false;
                        string propertiesContent = String.Empty;
                        while ((input = re.ReadLine()) != null && read)
                        {
                            if (input.Contains("EDITOR PROPERTIES"))
                            {
                                readingProperties = true;
                            }
                            else if (readingProperties)
                            {
                                propertiesContent += input.Replace("\n", "") + "\n";
                            }
                            else
                            {
                                _content += input.Replace("\n", "") + "\n";

                            }
                        }
                        re.Close();

                        // update properties
                        if (updateStyleProperties)
                        {
                            if (propertiesContent != String.Empty)
                            {
                                parseProperties(propertiesContent);
                            }
                        }
                    }

            }
            dr.Close();

        }

        private void parseProperties(string propertiesContent)
        {
            MatchCollection m =
    Regex.Matches(propertiesContent, "([^{]*){([^}]*)}", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);

            foreach (Match match in m)
            {
                GroupCollection groups = match.Groups;
                string cssClass = groups[1].Value.Replace("\n", "").Replace("\r", "").Trim().Trim(Environment.NewLine.ToCharArray());
                string cssCode = groups[2].Value.Trim(Environment.NewLine.ToCharArray());
                foreach (StylesheetProperty sp in this.Properties)
                    if (sp.Alias == cssClass)
                        sp.value = cssCode;
            }
        }

        public static StyleSheet MakeNew(BusinessLogic.User user, string Text, string FileName, string Content)
        {

            // Create the umbraco node
            CMSNode newNode = CMSNode.MakeNew(-1, moduleObjectType, user.Id, 1, Text, Guid.NewGuid());

            // Create the stylesheet data
            SqlHelper.ExecuteNonQuery("insert into cmsStylesheet (nodeId, filename, content) values ('" + newNode.Id.ToString() + "','" + FileName + "',@content)", SqlHelper.CreateParameter("@content", Content));

            // save to file to avoid file coherency issues
            StyleSheet newCss = new StyleSheet(newNode.Id, false, false);
            NewEventArgs e = new NewEventArgs();
            newCss.OnNew(e);

            return newCss;
        }

        public static StyleSheet[] GetAll()
        {

            ArrayList dbStylesheets = new ArrayList();

            Guid[] topNodeIds = CMSNode.TopMostNodeIds(moduleObjectType);
            //StyleSheet[] retval = new StyleSheet[topNodeIds.Length];
            for (int i = 0; i < topNodeIds.Length; i++)
            {
                //retval[i] = new StyleSheet(topNodeIds[i]);
                dbStylesheets.Add(new StyleSheet(topNodeIds[i]).Text.ToLower());
            }

            ArrayList fileStylesheets = new ArrayList();
            DirectoryInfo fileListing = new DirectoryInfo(System.Web.HttpContext.Current.Server.MapPath(GlobalSettings.Path + "/../css/"));

            foreach (FileInfo file in fileListing.GetFiles("*.css"))
            {
                if (!dbStylesheets.Contains(file.Name.Replace(file.Extension, "").ToLower()))
                {
                    fileStylesheets.Add(file.Name.Replace(file.Extension, ""));
                }
            }

            StyleSheet[] retval = new StyleSheet[dbStylesheets.Count + fileStylesheets.Count];
            for (int i = 0; i < topNodeIds.Length; i++)
            {
                retval[i] = new StyleSheet(topNodeIds[i]);

            }

            for (int i = 0; i < fileStylesheets.Count; i++)
            {

                string content = string.Empty;

                using (StreamReader re = File.OpenText(System.Web.HttpContext.Current.Server.MapPath(GlobalSettings.Path + "/../css/" + fileStylesheets[i].ToString() + ".css")))
                {
                    content = re.ReadToEnd();
                }

                retval[dbStylesheets.Count + i] = StyleSheet.MakeNew(new umbraco.BusinessLogic.User(0), fileStylesheets[i].ToString(), fileStylesheets[i].ToString(), content);
            }


            Array.Sort(retval, 0, retval.Length, new StyleSheetComparer());

            return retval;
        }



        public StylesheetProperty AddProperty(string Alias, BusinessLogic.User u)
        {
            return StylesheetProperty.MakeNew(Alias, this, u);
        }

        new public void delete()
        {
            DeleteEventArgs e = new DeleteEventArgs();
            FireBeforeDelete(e);

            if (!e.Cancel) {
                File.Delete(System.Web.HttpContext.Current.Server.MapPath(GlobalSettings.Path + "/../css/" + this.Text + ".css"));
                foreach (StylesheetProperty p in this.Properties)
                    p.delete();
                SqlHelper.ExecuteNonQuery("delete from cmsStylesheet where nodeId = @nodeId", SqlHelper.CreateParameter("@nodeId", this.Id));
                base.delete();

                FireAfterDelete(e);
            }

        }

        public void saveCssToFile()
        {
            StreamWriter SW;
            SW = File.CreateText(System.Web.HttpContext.Current.Server.MapPath(GlobalSettings.Path + "/../css/" + this.Text + ".css"));
            string tmpCss;
            //tmpCss = "/* GENERAL STYLES */\n";
            tmpCss = this.Content + "\n\n";
            tmpCss += "/* EDITOR PROPERTIES - PLEASE DON'T DELETE THIS LINE TO AVOID DUPLICATE PROPERTIES */\n";
            foreach (StylesheetProperty p in this.Properties)
            {
                tmpCss += p.ToString() + "\n";
            }
            SW.Write(tmpCss);
            SW.Close();
        }

        public XmlNode ToXml(XmlDocument xd)
        {
            XmlNode doc = xd.CreateElement("Stylesheet");
            doc.AppendChild(xmlHelper.addTextNode(xd, "Name", this.Text));
            doc.AppendChild(xmlHelper.addTextNode(xd, "FileName", this.Filename));
            doc.AppendChild(xmlHelper.addCDataNode(xd, "Content", this.Content));

            if (this.Properties.Length > 0)
            {
                XmlNode properties = xd.CreateElement("Properties");
                foreach (StylesheetProperty sp in this.Properties)
                {
                    XmlElement prop = xd.CreateElement("Property");
                    prop.AppendChild(xmlHelper.addTextNode(xd, "Name", sp.Text));
                    prop.AppendChild(xmlHelper.addTextNode(xd, "Alias", sp.Alias));
                    prop.AppendChild(xmlHelper.addTextNode(xd, "Value", sp.value));
                    properties.AppendChild(prop);
                }
                doc.AppendChild(properties);
            }

            return doc;
        }


        public static StyleSheet GetStyleSheet(int id, bool setupStyleProperties, bool loadContentFromFile)
        {
            return Cache.GetCacheItem<StyleSheet>(GetCacheKey(id), stylesheetCacheSyncLock,
    TimeSpan.FromMinutes(30),
    delegate
    {
        try
        {
            return new StyleSheet(id, setupStyleProperties, loadContentFromFile);
        }
        catch
        {
            return null;
        }
    });

        }

        private void InvalidateCache()
        {
            Cache.ClearCacheItem(GetCacheKey(this.Id));
        }

        private static string GetCacheKey(int id)
        {
            return UmbracoStylesheetCacheKey + id;
        }


        //EVENTS
        /// <summary>
        /// The save event handler
        /// </summary>
        public delegate void SaveEventHandler(StyleSheet sender, SaveEventArgs e);
        /// <summary>
        /// The new event handler
        /// </summary>
        public delegate void NewEventHandler(StyleSheet sender, NewEventArgs e);
        /// <summary>
        /// The delete event handler
        /// </summary>
        public delegate void DeleteEventHandler(StyleSheet sender, DeleteEventArgs e);


        /// <summary>
        /// Occurs when [before save].
        /// </summary>
        public static event SaveEventHandler BeforeSave;
        /// <summary>
        /// Raises the <see cref="E:BeforeSave"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void FireBeforeSave(SaveEventArgs e)
        {
            if (BeforeSave != null)
                BeforeSave(this, e);
        }

        /// <summary>
        /// Occurs when [after save].
        /// </summary>
        public static event SaveEventHandler AfterSave;
        /// <summary>
        /// Raises the <see cref="E:AfterSave"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void FireAfterSave(SaveEventArgs e)
        {
            if (AfterSave != null)
                AfterSave(this, e);
        }

        /// <summary>
        /// Occurs when [new].
        /// </summary>
        public static event NewEventHandler New;
        /// <summary>
        /// Raises the <see cref="E:New"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void OnNew(NewEventArgs e)
        {
            if (New != null)
                New(this, e);
        }

        /// <summary>
        /// Occurs when [before delete].
        /// </summary>
        public static event DeleteEventHandler BeforeDelete;
        /// <summary>
        /// Raises the <see cref="E:BeforeDelete"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void FireBeforeDelete(DeleteEventArgs e)
        {
            if (BeforeDelete != null)
                BeforeDelete(this, e);
        }

        /// <summary>
        /// Occurs when [after delete].
        /// </summary>
        public static event DeleteEventHandler AfterDelete;
        /// <summary>
        /// Raises the <see cref="E:AfterDelete"/> event.
        /// </summary>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected virtual void FireAfterDelete(DeleteEventArgs e)
        {
            if (AfterDelete != null)
                AfterDelete(this, e);
        }
    }


    public class StyleSheetComparer : IComparer
    {
        public StyleSheetComparer()
        {
            //default constructor
        }

        public Int32 Compare(Object pFirstObject, Object pObjectToCompare)
        {
            if (pFirstObject is StyleSheet)
            {
                return String.Compare(((StyleSheet)pFirstObject).Text, ((StyleSheet)pObjectToCompare).Text);
            }
            else
            {
                return 0;
            }
        }
    }

}
